// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include <zencore/zencore.h>

#include <fmt/args.h>
#include <string_view>

namespace zen {

namespace assert {
	template<typename... T>
	auto AssertCaptureArguments(T&&... Args)
	{
		return fmt::make_format_args(Args...);
	}

	void ExecAssertFmt
		[[noreturn]] (const char* Filename, int LineNumber, const char* FunctionName, std::string_view Format, fmt::format_args Args);

	// MSVC (v19.00.24215.1 at time of writing) ignores no-inline attributes on
	// lambdas. This can be worked around by calling the lambda from inside this
	// templated (and correctly non-inlined) function.
	template<typename RetType = void, class InnerType, typename... ArgTypes>
	RetType ZEN_FORCENOINLINE ZEN_DEBUG_SECTION CallColdNoInline(InnerType&& Inner, ArgTypes const&... Args)
	{
		return Inner(Args...);
	}

}  // namespace assert

#define ZEN_ASSERT_FORMAT(x, fmt, ...)                                                    \
	do                                                                                    \
	{                                                                                     \
		using namespace std::literals;                                                    \
		if (x) [[likely]]                                                                 \
			break;                                                                        \
		zen::assert::CallColdNoInline([&]() ZEN_FORCEINLINE {                             \
			zen::assert::ExecAssertFmt(__FILE__,                                          \
									   __LINE__,                                          \
									   __FUNCTION__,                                      \
									   "assert(" #x ") failed: " fmt ""sv,                \
									   zen::assert::AssertCaptureArguments(__VA_ARGS__)); \
		});                                                                               \
	} while (false)

}  // namespace zen
